﻿#include "WebSocket.h"
#include <QFileInfo>
#include <QWebSocket>
#include <QWebSocketServer>
#include "ShowWidget.h"
#include "log.h"
#include "notify.qpb.h"

WebSocket* WebSocket::instance = nullptr;
WebSocket* WebSocket::get_instance()
{
    if (instance == nullptr) {
        instance = new WebSocket();
    }
    return instance;
}

/// @brief 初始化 WebSocket 服务器
bool WebSocket::init()
{
    bool ok = socket_server.listen(QHostAddress::Any, WEB_SOCKET_PORT);
    if (!ok) {
        qDebug() << "WebSocket server failed to start: " << socket_server.errorString();
        show_widget->ADD_LOG_MESSAGE("WebSocket server failed to start: " + socket_server.errorString());
        return false;
    }
    /* 处理信号槽 */
    /* 新连接到来 */
    connect(&socket_server, &QWebSocketServer::newConnection, this, [=]() {
        QWebSocket* socket = socket_server.nextPendingConnection();
        qDebug() << "New WebSocket connection established from:" << socket->peerAddress().toString() << ":" << socket->peerPort();
        show_widget->ADD_LOG_MESSAGE("New WebSocket connection established from:" + socket->peerAddress().toString() + ":" + socket->peerPort());
        /* 连接断开 */
        connect(socket, &QWebSocket::disconnected, this, [=]() {
            qDebug() << "WebSocket connection closed from:" << socket->peerAddress().toString() << ":" << socket->peerPort();
            show_widget->ADD_LOG_MESSAGE("WebSocket connection closed from:" + socket->peerAddress().toString() + ":" + socket->peerPort());
            disconnect(this, &WebSocket::send_txt_message, this, nullptr);  // 需要解除信号, 不然客户端重复连接服务器, 服务求就会对同一个socket对象处理, 导致崩溃
            disconnect(this, &WebSocket::delete_friend, this, nullptr);
            disconnect(this, &WebSocket::send_friend_apply, this, nullptr);
            disconnect(this, &WebSocket::send_friend_apply_process, this, nullptr);
            disconnect(this, &WebSocket::send_create_group, this, nullptr);
            disconnect(this, &WebSocket::send_image_message, this, nullptr);
            disconnect(this, &WebSocket::send_file_message, this, nullptr);
            disconnect(this, &WebSocket::send_speech_message, this, nullptr);
            socket->deleteLater();  // 释放资源
        });
        /* 发生错误 */
        connect(socket, &QWebSocket::errorOccurred, this, [=](QAbstractSocket::SocketError error) {
            qDebug() << "WebSocket error occurred:" << socket->errorString();
            show_widget->ADD_LOG_MESSAGE("WebSocket error occurred:" + socket->errorString());
        });
        /* 收到消息 */
        connect(socket, &QWebSocket::textMessageReceived, this, [=](const QString& message) {
            qDebug() << "Received message:" << message;
            show_widget->ADD_LOG_MESSAGE("Received message:" + message);
            socket->sendTextMessage("Echo: " + message);  // 回显消息
        });
        /* 收到二进制消息 */
        connect(socket, &QWebSocket::binaryMessageReceived, this, [=](const QByteArray& message) {
            qDebug() << "Received binary message of size:" << message.size();
            show_widget->ADD_LOG_MESSAGE("Received binary message of size:" + QString::number(message.size()));
            socket->sendBinaryMessage(message);  // 回显二进制消息
        });
        /* 发送文本消息 */
        connect(this, &WebSocket::send_txt_message, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyEventId("");  // 不需要设置通知事件id
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY);
            chat::NotifyNewMessage new_message;
            const QByteArray       avatar       = read_file(":/image/defaultAvatar.png");
            chat::MessageInfo      message_info = *make_message_info(
                index++, "50001", "10099", avatar);  // 发送到第一个会话聊天当中
            new_message.setMessageInfo(message_info);
            message.setNewMessageInfo(new_message);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send text message to client...", INFO);
        });
        /* 删除好友, 这里指定删除10001 */
        connect(this, &WebSocket::delete_friend, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyEventId("");  // 不需要设置通知事件id
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::FRIEND_REMOVE_NOTIFY);
            NotifyFriendRemove friend_remove;
            friend_remove.setUserId("10001");
            message.setFriendRemove(friend_remove);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send remove friend event to client...", INFO);
        });
        /* 发送好友申请, 这里随便指定一个新的没有使用过的ID */
        connect(this, &WebSocket::send_friend_apply, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY);
            NotifyFriendAddApply friend_apply;
            QByteArray           avatar = read_file(":/image/vanilla.png");
            friend_apply.setUserInfo(*make_user_info(
                avatar, "10099", "Vanilla", "I'm a vanilla.",
                "13712345678"));
            message.setFriendAddApply(friend_apply);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send friend apply event to client...", INFO);
        });
        /* 发送是否同意好友申请 */
        connect(this, &WebSocket::send_friend_apply_process, this, [=](bool agree) {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY);
            NotifyFriendAddProcess friend_apply_process;
            QByteArray             avatar = read_file(":/image/vanilla.png");
            friend_apply_process.setUserInfo(*make_user_info(
                avatar, "10099", "Vanilla", "Vanilla Special",
                "13712345678"));
            friend_apply_process.setAgree(agree);
            message.setFriendProcessResult(friend_apply_process);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send friend apply process event to client...", INFO);
        });
        /* 发送创建群聊的通知 */
        connect(this, &WebSocket::send_create_group, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::CHAT_SESSION_CREATE_NOTIFY);
            NotifyNewChatSession new_chat_session;
            ChatSessionInfo      info;
            const QByteArray     avatar = read_file(":/image/may.png");
            info.setSingleChatFriendId("100050");
            info.setChatSessionId("500050");
            info.setChatSessionName("It's MyGo " + QString::number(50));
            MessageInfo message_info = *make_message_info(50, info.chatSessionId(), "100050", avatar);
            info.setPrevMessage(message_info);
            info.setAvatar(avatar);
            new_chat_session.setChatSessionInfo(info);
            message.setNewChatSessionInfo(new_chat_session);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send create group event to client...", INFO);
        });
        // 发送图片消息
        connect(this, &WebSocket::send_image_message, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyEventId("");  // 不需要设置通知事件id
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY);
            chat::NotifyNewMessage new_message;
            MessageInfo message_info = *make_image_info(
                index++, "50001", "10099", read_file(":/image/defaultAvatar.png"));  // 发送到第一个会话聊天当中
            new_message.setMessageInfo(message_info);
            message.setNewMessageInfo(new_message);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send image message to client...", INFO);
        });
        // 发送文件消息
        connect(this, &WebSocket::send_file_message, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyEventId("");  // 不需要设置通知事件id
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY);
            chat::NotifyNewMessage new_message;
            MessageInfo            message_info = *make_file_info(
                index++, "50001", "10099", read_file(":/image/defaultAvatar.png"));  // 发送到第一个会话聊天当中
            new_message.setMessageInfo(message_info);
            message.setNewMessageInfo(new_message);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send file message to client...", INFO);
        });
        // 发送语音消息
        connect(this, &WebSocket::send_speech_message, this, [=] {
            if (socket == nullptr || !socket->isValid()) {
                show_widget->ADD_LOG_MESSAGE_LEVEL("Socket is not valid.", WARNING);
                return;
            }
            /* 构建pb结构体 */
            chat::NotifyMessage message;
            message.setNotifyEventId("");  // 不需要设置通知事件id
            message.setNotifyType(chat::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY);
            chat::NotifyNewMessage new_message;
            MessageInfo            message_info = *make_speech_info(
                index++, "50001", "10099", read_file(":/image/defaultAvatar.png"));  // 发送到第一个会话聊天当中
            new_message.setMessageInfo(message_info);
            message.setNewMessageInfo(new_message);
            /* 序列化并发送 */
            QByteArray body = message.serialize(&serializer);
            socket->sendBinaryMessage(body);
            show_widget->ADD_LOG_MESSAGE_LEVEL("Send speech message to client...", INFO);
        });
    });
    return true;
}

WebSocket::WebSocket()
    : socket_server("Qt Web Socket Server", QWebSocketServer::NonSecureMode)
{
    show_widget = ShowWidget::get_instance();
}

WebSocket::~WebSocket()
{
}

/// @brief 读取文件内容
/// @return 是否读取成功
///     @retval "" 表示读取失败
///     @retval 不是上面的情况表示读取成功
QByteArray WebSocket::read_file(const QString& file_name)
{
    QFile file(file_name);
    bool  ok = file.open(QIODevice::ReadOnly);
    if (ok == false) {
        LOG() << "open file: " << file_name << "error! why: " << file.errorString();
        show_widget->ADD_LOG_MESSAGE("open file: " + file_name + "error! why: " + file.errorString());
        file.close();
        return QByteArray();
    }
    QByteArray data = file.readAll();
    file.close();
    return data;
}

/// @brief 获得当前的时间戳
qint64 WebSocket::get_current_time_stamp()
{
    return QDateTime::currentSecsSinceEpoch();
}

/// @brief 发送数据到客户端
QHttpServerResponse WebSocket::make_http_response(const QByteArray& data)
{
    auto         http_resp = QHttpServerResponse(data, QHttpServerResponse::StatusCode::Ok);
    QHttpHeaders headers;
    headers.append(QHttpHeaders::WellKnownHeader::ContentType, "application/x-protobuf");
    http_resp.setHeaders(headers);
    return http_resp;
}

/// @brief 工具方法: 创建一个UserInfo对象
std::unique_ptr<UserInfo> WebSocket::make_user_info(const QByteArray& avatar, const QString& user_id,
                                                    const QString& description, const QString& nick_name,
                                                    const QString& phone)
{
    auto user_info = std::make_unique<UserInfo>();
    user_info->setAvatar(avatar);
    user_info->setUserId(user_id);
    user_info->setDescription(description);
    user_info->setNickname(nick_name);
    user_info->setPhone(phone);
    /* C++11 以后，函数按值返回局部变量时，
    编译器会自动触发 返回值优化（RVO） 或 移动构造，不会调用拷贝构造函数，
    也不会违反 unique_ptr 的“唯一所有权”语义。 */
    return user_info;
}

/// @brief 工具方法: 创建一个文本消息类型
std::unique_ptr<MessageInfo> WebSocket::make_message_info(int index, const QString& chat_session_id, const QString& user_id, const QByteArray& avatar)
{
    auto message_info = std::make_unique<MessageInfo>();
    message_info->setMessageId(QString::number(index) + "2000");  // random
    message_info->setChatSessionId(chat_session_id);
    message_info->setTimestamp(get_current_time_stamp());
    message_info->setSender(*make_user_info(avatar, user_id,
                                            "desc..." + QString::number(index), "FooBar", "114514"));
    MessageContent message_content;
    message_content.setMessageType(MessageTypeGadget::MessageType::STRING);
    StringMessageInfo string_message_info;
    QString           text_message = QString("你好" + QString::number(index));
    string_message_info.setContent(text_message);
    message_content.setStringMessage(string_message_info);
    message_info->setMessage(message_content);
    return message_info;
}

/// @brief 生成一个图片消息类型
std::unique_ptr<MessageInfo> WebSocket::make_image_info(int index, const QString& chat_session_id, const QString& user_id, const QByteArray& avatar)
{
    auto message_info = std::make_unique<MessageInfo>();
    message_info->setMessageId(QString::number(index) + "2000");  // random
    message_info->setChatSessionId(chat_session_id);
    message_info->setTimestamp(get_current_time_stamp());
    message_info->setSender(*make_user_info(avatar, user_id,
                                            "desc..." + QString::number(index), "FooBar", "114514"));
    MessageContent message_content;
    message_content.setMessageType(MessageTypeGadget::MessageType::IMAGE);
    ImageMessageInfo image_message_info;
    image_message_info.setFileId("test_image");
    message_content.setImageMessage(image_message_info);
    message_info->setMessage(message_content);
    return message_info;
}

/// @brief 生成一个文件消息类型
std::unique_ptr<MessageInfo> WebSocket::make_file_info(int index, const QString& chat_session_id, const QString& user_id, const QByteArray& avatar)
{
    auto message_info = std::make_unique<MessageInfo>();
    message_info->setMessageId(QString::number(index) + "3000");  // random
    message_info->setChatSessionId(chat_session_id);
    message_info->setTimestamp(get_current_time_stamp());
    message_info->setSender(*make_user_info(avatar, user_id,
                                            "desc..." + QString::number(index), "FooBar", "114514"));
    MessageContent message_content;
    message_content.setMessageType(MessageTypeGadget::MessageType::FILE);
    FileMessageInfo file_message_info;
    file_message_info.setFileId("test_file");
    file_message_info.setFileName("test.txt");
    message_content.setFileMessage(file_message_info);
    message_info->setMessage(message_content);
    return message_info;
}

std::unique_ptr<MessageInfo> WebSocket::make_speech_info(int index, const QString& chat_session_id, const QString& user_id, const QByteArray& avatar)
{
    auto message_info = std::make_unique<MessageInfo>();
    message_info->setMessageId(QString::number(index) + "4000");  // random
    message_info->setChatSessionId(chat_session_id);
    message_info->setTimestamp(get_current_time_stamp());
    message_info->setSender(*make_user_info(avatar, user_id,
                                            "desc..." + QString::number(index), "FooBar", "114514"));
    MessageContent message_content;
    message_content.setMessageType(MessageTypeGadget::MessageType::SPEECH);
    SpeechMessageInfo speech_message_info;
    speech_message_info.setFileId("test_speech");
    message_content.setSpeechMessage(speech_message_info);
    message_info->setMessage(message_content);
    return message_info;
}
